package com.main.tcp;

import com.constant.ModbusConstant;
import com.exception.ModbusException;
import com.function.base.BaseFunctionAction;
import com.listener.DataReceiveListener;
import com.listener.DataSendListener;
import com.main.ModbusClient;
import com.response.JsonResult;
import com.response.ResponseData;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * tcp 功能实现主类
 * @author zhujh
 */
public class ModbusTcpClient implements ModbusClient {

    private int port;
    private String modbusIp;
    private Socket socket = new Socket();
    private int connectTimeout = ModbusConstant.CONNECT_TIMEOUT;

    private InputStream inputStream;
    private DataOutputStream outputStream;

    private byte[] dataSend;
    private byte[] dataReceive;

    private List<DataSendListener> dataSendListeners = new ArrayList<>();
    private List<DataReceiveListener> dataReceiveListeners = new ArrayList<>();
//    private ModbusFunctionInterface functionInterface = new ModbusTcpFunction();

    public ModbusTcpClient() {}

    public ModbusTcpClient(String modbusIp, int port) {
        this.modbusIp = modbusIp;
        this.port = port;
    }

    public JsonResult process(BaseFunctionAction functionAction) throws IOException, ModbusException {
        functionAction.validateSendDataParams();

        byte[] request = functionAction.getReQuestByte();
        byte[] response = this.getResponse(request, request.length);

        functionAction.validateResponse(response);

        List<ResponseData> data = functionAction.getResPonseData(response);
        return JsonResult.getSuccessResult(data);

    }


    @Override
    public byte[] getResponse(byte[] dataSend, int length) throws IOException {
        this.outputStream.write(dataSend, 0, length);

        if (!this.dataSendListeners.isEmpty()) {
            this.dataSend = new byte[length];
            System.arraycopy(dataSend, 0, this.dataSend, 0, length);
            this.dataSendListeners.forEach(listener -> listener.onDataSend(dataSend));
        }

        byte[] dataReceive = new byte[2100];
        int bytesCount = this.inputStream.read(dataReceive, 0, dataReceive.length);

        if (!this.dataReceiveListeners.isEmpty()) {
            this.dataReceive = new byte[bytesCount];
            System.arraycopy(dataReceive, 0, this.dataReceive, 0, bytesCount);
            this.dataReceiveListeners.forEach(listener -> listener.onDataReceive(dataReceive));
        }

        return dataReceive;
    }



    public void connect() throws IOException {
        Socket socket = new Socket(this.modbusIp, this.port);
        socket.setSoTimeout(this.connectTimeout);

        this.socket = socket;
        this.outputStream = new DataOutputStream(this.socket.getOutputStream());
        this.inputStream = this.socket.getInputStream();
    }


    public void connect(String modbusIp, int port) throws IOException {
        this.port = port;
        this.modbusIp = modbusIp;
        this.connect();
    }


    public void disconnect() throws IOException {
        if (this.inputStream != null) {
            this.inputStream.close();
        }

        if (this.outputStream != null) {
            this.outputStream.close();
        }

        if (this.socket != null) {
            this.socket.close();
        }

        this.socket = null;
    }


    public boolean isConnected() {
        return this.socket != null && this.socket.isConnected();
    }

//    static private class SingletonHolder {
//        final static private ModbusTcpClient instance = new ModbusTcpClient();
//    }
//
//    static public ModbusTcpClient getInstance() {
//        return SingletonHolder.instance;
//    }
}
